package com.cloudkinto.service.product.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.bo.order.inbound.NormalReceiptConfirmBo;
import com.cloudkinto.bo.order.inbound.NormalReceiptConfirmDetailSkuBo;
import com.cloudkinto.bo.order.inbound.NormalReceiptConfirmNewProductBo;
import com.cloudkinto.bo.print.PrintSkuBo;
import com.cloudkinto.bo.product.*;
import com.cloudkinto.bo.stock.ProductStockBySkuQueryBo;
import com.cloudkinto.common.BeanConvert;
import com.cloudkinto.common.ExcelExportTitleStyle;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.ExcelUtils;
import com.cloudkinto.common.utils.StringsUtil;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.*;
import com.cloudkinto.dto.product.*;
import com.cloudkinto.dto.stock.query.SkuStockPageDto;
import com.cloudkinto.entity.*;
import com.cloudkinto.enumType.common.CountryEnum;
import com.cloudkinto.enumType.operate.OperateTypeEnum;
import com.cloudkinto.enumType.product.DangerousEnum;
import com.cloudkinto.service.company.CompanyService;
import com.cloudkinto.service.file.FileService;
import com.cloudkinto.service.operate.OperateRecordService;
import com.cloudkinto.service.product.ProductService;
import com.cloudkinto.service.product.vo.ProductMsgVo;
import com.cloudkinto.utils.BarCodePdfUtils;
import com.cloudkinto.utils.BeanCopierUtils;
import com.cloudkinto.vo.api.ProductApiVo;
import com.cloudkinto.vo.common.CommonBatchOperationVo;
import com.cloudkinto.vo.company.CompanyRes;
import com.cloudkinto.vo.order.returns.print.PrintSkuEt;
import com.cloudkinto.vo.product.ProductVo;
import com.cloudkinto.vo.stock.ProductTransportVo;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 产品表 service实现类
 * </p>
 *
 * @author long
 * @since 2024-10-29
 */
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class ProductServiceImpl extends ServiceImpl<ProductDao, ProductDo> implements ProductService {

    static HashMap<Integer, String> approveStatusMap;

    static {
        approveStatusMap = new HashMap<>();
        approveStatusMap.put(0, "草稿");
        approveStatusMap.put(1, "审核中");
        approveStatusMap.put(2, "已审核");
        approveStatusMap.put(3, "已驳回");
        approveStatusMap.put(4, "废弃");
    }

    private final FileService fileService;
    private final ProductCategoryDao productCategoryDao;
    private final ProductStockDao productStockDao;
    private final OrderBoxProductDao boxProductDao;
    private final OrderOutboundProductDao outboundProductDao;
    private final OrderReturnProductDao returnProductDao;
    private final MaterialDao materialDao;
    private final WarehouseDao warehouseDao;
    private final OperateRecordService operateRecordService;
    private final CompanyService companyService;

    @Override
    public SingleResult<Map<String, Object>> addInit() {
        Map<String, Object> map = new HashMap<>(4);
        //所属危险品
        map.put("dangerousList", DangerousEnum.getList());
        //国家
        map.put("countryRegion", CountryEnum.getCountryEnumList());
        return SingleResult.success(map);
    }

    @Override
    public void add(ProductAddBo bo, Long userId, String userName, Long companyId, Long tenantId) {
//        ProductDo entityDo = new ProductDo();
//        BeanUtils.copyProperties(bo, entityDo);
        ProductDo entityDo = BeanConvert.INSTANCE.productAddBoToProductDo(bo);
        //sku判断是否存在
        boolean skuAvailable = this.skuAvailable(entityDo.getSku(), companyId, tenantId);
        if (!skuAvailable) {
            throw new BizException(SysConstant.Sku_Already_Exist);
        }
        if (!bo.isNeedPack()) {
            entityDo.setPackInfo("[]");
        }
        entityDo.setCompanyId(companyId);
        entityDo.setTenantId(tenantId);
        //判断当前客户是否开启免审
        CompanyDo one = companyService.getOne(
                Wrappers.<CompanyDo>lambdaQuery()
                        .select(CompanyDo::getProductFreeTrial)
                        .eq(CompanyDo::getId, companyId)
        );
        boolean productFreeTrial = Objects.nonNull(one) && one.getProductFreeTrial() == 0;
        entityDo.setExemptCheck(productFreeTrial);
        if (Objects.nonNull(bo.getPicId())) {
            //上传到真实路径
            fileService.uploadFile(bo.getPicId());
        }
        if (productFreeTrial && entityDo.getApproveStatus().equals(1)) {
            entityDo.setApproveStatus(2);
        }
        baseMapper.insert(entityDo);
//        operateRecordService.operateAddRecordAndValue(
//                SysConstant.Product_Pass_Without_Trial,
//                "",
//                entityDo.getId(),
//                userId,
//                userName,
//                OperateTypeEnum.Product
//        );
        if (entityDo.getApproveStatus().equals(1)) {
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Product_Submission_For_Review,
                    "",
                    entityDo.getId(),
                    userId,
                    userName,
                    OperateTypeEnum.Product
            );
        }
        //免审操作日志
        if (productFreeTrial) {
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Product_Pass_Without_Trial,
                    "",
                    entityDo.getId(),
                    1L,
                    "system",
                    OperateTypeEnum.Product
            );
        }
    }

    @Override
    public void update(ProductUpdateBo bo, Long userId, String username, Long tenantId) {
        ProductDo oldDo = baseMapper.selectById(bo.getId());
        ProductDo newDo = new ProductDo();
//        BeanUtils.copyProperties(bo, newDo);
        BeanConvert.INSTANCE.productUpdateBoToProductDo(bo, newDo);
        if (StrUtil.isBlank(bo.getPackInfo())) {
            newDo.setPackInfo("[]");
        }
        if (Objects.nonNull(bo.getPicId())) {
            if (Objects.isNull(oldDo.getPicId()) || !oldDo.getPicId().equals(bo.getPicId())) {
                fileService.deleteFile(oldDo.getPicId());
                fileService.uploadFile(bo.getPicId());
            }
        }
        if (!bo.getNeedPack()) {
            newDo.setPackInfo("[]");
        }
        baseMapper.updateById(newDo);
        //记录日志
        if (Objects.isNull(oldDo.getWmsLength()) && Objects.nonNull(newDo.getWmsLength())) {
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Add_Wms_Product_Measure,
                    newDo.getWmsLength() + SysConstant.operateRecordSeparator
                    + newDo.getWmsWidth() + SysConstant.operateRecordSeparator
                    + newDo.getWmsHeight() + SysConstant.operateRecordSeparator
                    + newDo.getWmsWeight(),
                    newDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
            //修改测量时间
            newDo.setWmsMeasureTime(new Date());
            baseMapper.updateById(newDo);
        } else if (Objects.nonNull(oldDo.getLength()) && Objects.nonNull(newDo.getLength()) && (oldDo.getLength().compareTo(newDo.getLength()) != 0 ||
                                                                                                oldDo.getWidth().compareTo(newDo.getWidth()) != 0 ||
                                                                                                oldDo.getHeight().compareTo(newDo.getHeight()) != 0 ||
                                                                                                oldDo.getWeight().compareTo(newDo.getWeight()) != 0)) {
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Modify_Oms_Product_Measure,
                    oldDo.getLength() + SysConstant.operateRecordSeparator
                    + oldDo.getWidth() + SysConstant.operateRecordSeparator
                    + oldDo.getHeight() + SysConstant.operateRecordSeparator
                    + newDo.getLength() + SysConstant.operateRecordSeparator
                    + newDo.getWidth() + SysConstant.operateRecordSeparator
                    + newDo.getHeight() + SysConstant.operateRecordSeparator
                    + oldDo.getWeight() + SysConstant.operateRecordSeparator
                    + newDo.getWeight(),
                    newDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
        } else if (Objects.nonNull(oldDo.getWmsLength())
                   && Objects.nonNull(newDo.getWmsLength())
                   && (oldDo.getWmsLength().compareTo(newDo.getWmsLength()) != 0 ||
                       oldDo.getWmsWidth().compareTo(newDo.getWmsWidth()) != 0 ||
                       oldDo.getWmsHeight().compareTo(newDo.getWmsHeight()) != 0 ||
                       oldDo.getWmsWeight().compareTo(newDo.getWmsWeight()) != 0)) {
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Modify_Wms_Product_Measure,
                    oldDo.getWmsLength() + SysConstant.operateRecordSeparator
                    + oldDo.getWmsWidth() + SysConstant.operateRecordSeparator
                    + oldDo.getWmsHeight() + SysConstant.operateRecordSeparator
                    + newDo.getWmsLength() + SysConstant.operateRecordSeparator
                    + newDo.getWmsWidth() + SysConstant.operateRecordSeparator
                    + newDo.getWmsHeight() + SysConstant.operateRecordSeparator
                    + oldDo.getWmsWeight() + SysConstant.operateRecordSeparator
                    + newDo.getWmsWeight(),
                    newDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
        } else if (StrUtil.isBlank(oldDo.getPackInfo()) && StrUtil.isNotBlank(newDo.getPackInfo()) ||
                   StrUtil.isNotBlank(oldDo.getPackInfo()) && StrUtil.isNotBlank(newDo.getPackInfo()) &&
                   !oldDo.getPackInfo().equals(newDo.getPackInfo())) {
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Product_Pack_Info_Change,
                    "",
                    newDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
        } else {
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Product_Update_Info,
                    "",
                    newDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
        }
    }

    @Override
    public int delete(Long id, Long userId, Long tenantId) {
        return baseMapper.deleteById(id);
    }

    @Override
    public ProductVo detail(Long id) {
        ProductDo entityDo = baseMapper.selectById(id);
        if (entityDo == null) {
            return null;
        }
//        ProductVo res = new ProductVo();
//        BeanUtils.copyProperties(entityDo, res);
        ProductVo res = BeanConvert.INSTANCE.productDoToProductVo(entityDo);
        res.setOperateLogList(operateRecordService.getOperateList(id, OperateTypeEnum.Product));
        if (StrUtil.isNotBlank(res.getFnSku())) {
            res.setFnskuList(res.getFnSku().split(","));
        }
        CompanyDo companyDo = companyService.getById(res.getCompanyId());
        res.setCustomerName(companyDo.getName());
        res.setCustomerCode(companyDo.getCompanyCode());
        if (Objects.nonNull(entityDo.getPicId())) {
            res.setProductUrl(fileService.getFilePath(entityDo.getPicId()));
        }
        if (Objects.nonNull(entityDo.getCategoryId())){
            ProductCategoryDo productCategoryDo = productCategoryDao.selectById(entityDo.getCategoryId());
            if (Objects.nonNull(productCategoryDo)) {
                res.setFullPathName(productCategoryDo.getFullPathName());
            }
        }
        return res;
    }

    @Override
    public SingleResult pageInit(Long userId, Long tenantId, String source) {
        HashMap<String, Object> map = new HashMap<>(4);
        map.put("customerCodeList", companyService.getCompanyList(userId, tenantId, source));
        map.put("approveStatusMap", approveStatusMap);
        return SingleResult.success(map);
    }

    @Override
    public PageResult getListPage(ProductQueryBo bo) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<ProductDo> records = baseMapper.getListPage(bo);
        PageInfo pageInfo = new PageInfo<>(records);
        return new PageResult(
                this.handlerPageList(records),
                current,
                pageSize,
                (int) pageInfo.getTotal(),
                pageInfo.getPages()
        );
    }

    @Override
    public PageResult getListPageApi(ProductQueryBo bo) {
        PageResult pageResult = this.getListPage(bo);
        List<ProductApiVo> apiVoList = BeanCopierUtils.listCopyMapper(pageResult.getData(), ProductVo.class, ProductApiVo.class);
        return new PageResult(
                apiVoList,
                pageResult.getPageInfo().getCurrent(),
                pageResult.getPageInfo().getPageSize(),
                pageResult.getPageInfo().getTotal(),
                pageResult.getPageInfo().getPageNos()
        );
    }

    private List<ProductVo> handlerPageList(List<ProductDo> records) {
        List<ProductVo> data = JSONUtil.toList(JSONUtil.toJsonStr(records), ProductVo.class);
        if (CollUtil.isNotEmpty(data)) {
            //客户
            Map<Long, CompanyDo> comMap = companyService.lambdaQuery()
                    .in(CompanyDo::getId, data.stream().map(ProductVo::getCompanyId).collect(Collectors.toSet()))
                    .select(CompanyDo::getId, CompanyDo::getName, CompanyDo::getCompanyCode)
                    .list().stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
            //分类
            Set<Long> categoryIdList = data.stream().map(ProductVo::getCategoryId).filter(Objects::nonNull).collect(Collectors.toSet());
            Map<Long, ProductCategoryDo> categoryMatch = productCategoryDao.selectList(Wrappers.<ProductCategoryDo>lambdaQuery()
                    .in(CollUtil.isNotEmpty(categoryIdList), ProductCategoryDo::getId, categoryIdList)
            ).stream().collect(Collectors.toMap(ProductCategoryDo::getId, val -> val));
            //产品图片
            Map<Long, String> filePathMatch = fileService.getFilePath(data.stream().map(ProductVo::getPicId).filter(Objects::nonNull).collect(Collectors.toList()));
            data.forEach(item -> {
                CompanyDo companyDo = comMap.get(item.getCompanyId());
                if (companyDo == null) {
                    item.setCustomerCode("");
                    item.setCustomerName("未知");
                } else {
                    item.setCustomerCode(companyDo.getCompanyCode());
                    item.setCustomerName(companyDo.getName());
                }
                item.setProductId(item.getId());
                ProductCategoryDo productCategoryDo = categoryMatch.get(item.getCategoryId());
                if (Objects.nonNull(productCategoryDo)) {
                    item.setFullPathName(productCategoryDo.getFullPathName());
                }
                if (Objects.nonNull(item.getPicId())) {
                    item.setProductUrl(filePathMatch.get(item.getPicId()));
                }
            });
        }
        return data;
    }

    @Override
    public boolean skuAvailable(String sku, Long companyId, Long tenantId) {
        ProductDo one = baseMapper.selectOne(
                Wrappers.<ProductDo>lambdaQuery()
                        .eq(ProductDo::getSku, sku)
                        .eq(ProductDo::getCompanyId, companyId)
                        .eq(ProductDo::getTenantId, tenantId)
        );
        return Objects.isNull(one);
    }

    @Override
    public SingleResult statusStatistics(ProductQueryBo bo) {
        List<ProductDo> records = baseMapper.getListPage(bo);
        Map<Integer, List<ProductDo>> dataListByStatus = records.stream().collect(Collectors.groupingBy(ProductDo::getApproveStatus));
        Map<Integer, Integer> map = new HashMap<>(8);
        for (Map.Entry<Integer, String> entry : approveStatusMap.entrySet()) {
            map.put(entry.getKey(), dataListByStatus.getOrDefault(entry.getKey(), Collections.emptyList()).size());
        }
        return SingleResult.success(map);
    }

    @Override
    public void approve(ProductApproveBo bo, Long userId, String username) {
        this.lambdaUpdate()
                .in(ProductDo::getId, bo.getIds())
                .set(ProductDo::getApproveStatus, bo.getApproveStatus())
                .set(StrUtil.isNotBlank(bo.getApproveReason()), ProductDo::getApproveRejectReason, bo.getApproveReason())
                .set(ProductDo::getApproveTime, LocalDateTime.now())
                .update();
        bo.getIds().forEach(
                id -> operateRecordService.operateAddRecordAndValue(
                        StrUtil.isNotBlank(bo.getApproveReason())
                                ? SysConstant.Product_Check_Refuse
                                : SysConstant.Product_Check_Agree,
                        StrUtil.isNotBlank(bo.getApproveReason())
                                ? bo.getApproveReason()
                                : "",
                        id,
                        userId,
                        username,
                        OperateTypeEnum.Product
                )
        );

    }

    @Override
    public CommonBatchOperationVo importUpdateProductOmp(MultipartFile file, Long companyId, Long userId, String username, Long tenantId) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<ProductImportUpdateOmpDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, ProductImportUpdateOmpDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream().filter(
                i -> StrUtil.isNotBlank(i.getSku())
                     && StrUtil.isNotBlank(i.getCompanyCode())
                     && Objects.nonNull(i.getLength())
                     && Objects.nonNull(i.getWidth())
                     && Objects.nonNull(i.getHeight())
                     && Objects.nonNull(i.getWeight())
                     && StrUtil.isNotBlank(i.getUnit())
        ).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", "Exception");
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Map<String, Long> companyCodeMatchId = companyService.lambdaQuery()
                .select(CompanyDo::getId, CompanyDo::getCompanyCode)
                .eq(CompanyDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(CompanyDo::getCompanyCode, CompanyDo::getId));
        for (ProductImportUpdateOmpDto dto : importDtoList) {
            if (!companyCodeMatchId.containsKey(dto.getCompanyCode())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getSku() + "_" + dto.getCompanyCode());
                map.put("errorMsg", "Customer Code/客户代码[" + dto.getCompanyCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            ProductDo productDo = this.getOne(Wrappers.<ProductDo>lambdaQuery()
                    .eq(ProductDo::getSku, dto.getSku())
                    .eq(ProductDo::getTenantId, tenantId)
                    .eq(ProductDo::getCompanyId, companyCodeMatchId.get(dto.getCompanyCode()))
                    .last("limit 1")
            );
            if (Objects.isNull(productDo)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getSku() + "_" + dto.getCompanyCode());
                map.put("errorMsg", "SKU[" + dto.getSku() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (!"公制（cm/kg）".equals(dto.getUnit()) && !"英制（inch/lb）".equals(dto.getUnit())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getSku() + "_" + dto.getCompanyCode());
                map.put("errorMsg", "Unit/单位 Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if ("公制（cm/kg）".equals(dto.getUnit())) {
                productDo.setWmsLength(dto.getLength());
                productDo.setWmsWidth(dto.getWidth());
                productDo.setWmsHeight(dto.getHeight());
                productDo.setWmsWeight(dto.getWeight());
                productDo.setWmsLengthBs(dto.getLength().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                productDo.setWmsWidthBs(dto.getWidth().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                productDo.setWmsHeightBs(dto.getHeight().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                productDo.setWmsWeightBs(dto.getWeight().multiply(BigDecimal.valueOf(2.2046)).setScale(3, RoundingMode.HALF_UP));
            } else {
                productDo.setWmsLength(dto.getLength().multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                productDo.setWmsWidth(dto.getWidth().multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                productDo.setWmsHeight(dto.getHeight().multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                productDo.setWmsWeight(dto.getWeight().divide(BigDecimal.valueOf(2.2046), 3, RoundingMode.HALF_UP));
                productDo.setWmsLengthBs(dto.getLength());
                productDo.setWmsWidthBs(dto.getWidth());
                productDo.setWmsHeightBs(dto.getHeight());
                productDo.setWmsWeightBs(dto.getWeight());
            }
            if ((StrUtil.isNotBlank(dto.getSpu()))) {
                productDo.setSpu(dto.getSpu());
            }
            if ((StrUtil.isNotBlank(dto.getJanCode()))) {
                productDo.setJanCode(dto.getJanCode());
            }
            if ((Objects.nonNull(dto.getBuiltInNumber()))) {
                productDo.setBuiltInNumber(dto.getBuiltInNumber());
            }
            if ((StrUtil.isNotBlank(dto.getItf()))) {
                productDo.setItf(dto.getItf());
            }
            this.updateById(productDo);
            res.setSuccess(res.getSuccess() + 1);
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Product_import_Update_Omp,
                    "",
                    productDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
        }
        return res;
    }

    @Override
    public List<ProductDo> normalReceiptConfirmNewProductKeepList(NormalReceiptConfirmBo bo, Long tenantId) {
        NewProductQueryBo queryBo = new NewProductQueryBo();
        List<String> skuList = new ArrayList<>();
        bo.getDetailList().forEach(detail -> {
            skuList.addAll(
                    detail.getSkuList().stream()
                            .map(NormalReceiptConfirmDetailSkuBo::getSku)
                            .collect(Collectors.toList())
            );
        });
        queryBo.setSkuList(skuList);
        return baseMapper.getNewProductKeepPageList(queryBo, tenantId);
    }

    @Override
    public PageResult getNewProductKeepPageList(NewProductQueryBo bo, Long tenantId) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<ProductDo> productDoList = baseMapper.getNewProductKeepPageList(bo, tenantId);
        PageInfo<ProductDo> pageInfo = new PageInfo<>(productDoList);
        return new PageResult<>(this.handlerPageList(productDoList), current, pageSize, (int) pageInfo.getTotal(), pageInfo.getPages());
    }

    @Override
    public void normalReceiptConfirmNewProductKeep(List<NormalReceiptConfirmNewProductBo> bo, Long userId, String username, Long tenantId) {
        bo.forEach(item -> {
//            ProductDo newDo = new ProductDo();
//            BeanUtils.copyProperties(item, newDo);
            ProductDo newDo = BeanConvert.INSTANCE.normalReceiptConfirmNewProductBoToProductDo(item);
            //修改测量时间
            newDo.setWmsMeasureTime(new Date());
            baseMapper.updateById(newDo);
            //记录日志
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Add_Wms_Product_Measure,
                    newDo.getWmsLength() + SysConstant.operateRecordSeparator
                    + newDo.getWmsWeight() + SysConstant.operateRecordSeparator
                    + newDo.getWmsHeight() + SysConstant.operateRecordSeparator
                    + newDo.getWmsWeight(),
                    newDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
        });
    }

    @Override
    public CommonBatchOperationVo importProduct(MultipartFile file, Long userId, String username, Long tenantId, Long companyId) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<ProductImportDtoV2> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, ProductImportDtoV2.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream().filter(
                i -> StrUtil.isNotBlank(i.getSku())
                     && StrUtil.isNotBlank(i.getProductName())
                     && StrUtil.isNotBlank(i.getJanCode())
                     && StrUtil.isNotBlank(i.getMainBarcode())
                     && StrUtil.isNotBlank(i.getUnit())
                     && Objects.nonNull(i.getWholesalePrice())
                     && Objects.nonNull(i.getCostPrice())
                     && StrUtil.isNotBlank(i.getCurrencyCode())
        ).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", "Exception");
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Map<String, ProductDo> skuMatchExist = this.lambdaQuery()
                .eq(ProductDo::getTenantId, tenantId)
                .eq(ProductDo::getCompanyId, companyId)
                .in(ProductDo::getSku, importDtoList.stream().map(ProductImportDtoV2::getSku).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(ProductDo::getSku, val -> val));
        //判断当前客户是否开启免审
        CompanyDo one = companyService.getOne(
                Wrappers.<CompanyDo>lambdaQuery()
                        .select(CompanyDo::getProductFreeTrial)
                        .eq(CompanyDo::getId, companyId)
        );
        Boolean productFreeTrial = Objects.nonNull(one) ? one.getProductFreeTrial() == 0 : Boolean.FALSE;
        for (ProductImportDtoV2 dto : importDtoList) {
            ProductDo exist = skuMatchExist.get(dto.getSku());
            if (Objects.nonNull(exist)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getSku());
                map.put("errorMsg", "SKU[" + dto.getSku() + "] Exists,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (!"公制 (cm/kg)".equals(dto.getUnit()) && !"英制 (inch/lb)".equals(dto.getUnit())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getSku());
                map.put("errorMsg", "Unit/单位 Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
//            ProductDo productDo = new ProductDo();
//            BeanUtils.copyProperties(dto, productDo);
            ProductDo productDo = BeanConvert.INSTANCE.productImportDtoV2ToProductDo(dto);
            if (StrUtil.isNotBlank(dto.getPrimaryCategory()) || StrUtil.isNotBlank(dto.getSecondaryCategory()) || StrUtil.isNotBlank(dto.getTertiaryCategory())) {
                String category = StrUtil.isNotBlank(dto.getPrimaryCategory())
                        ? dto.getPrimaryCategory() + (StrUtil.isNotBlank(dto.getSecondaryCategory()) ? "\\" + dto.getSecondaryCategory() + (StrUtil.isNotBlank(dto.getTertiaryCategory()) ? "\\" + dto.getTertiaryCategory() : "") : "")
                        : "";
                if (StrUtil.isNotBlank(category)) {
                    ProductCategoryDo productCategoryDo = productCategoryDao.selectOne(
                            Wrappers.<ProductCategoryDo>lambdaQuery()
                                    .eq(ProductCategoryDo::getFullPathName, category)
                    );
                    if (Objects.isNull(productCategoryDo)) {
                        HashMap<String, Object> map = new HashMap<>(4);
                        map.put("code", dto.getSku());
                        map.put("errorMsg", "Category/分类 Wrong,Please check!");
                        res.setFailed(res.getFailed() + 1);
                        res.getFailedList().add(map);
                        continue;
                    }
                    productDo.setCategoryId(productCategoryDo.getId());
                }
            }
            if ("公制 (cm/kg)".equals(dto.getUnit())) {
                productDo.setLength(BigDecimal.valueOf(dto.getLength()));
                productDo.setWidth(BigDecimal.valueOf(dto.getWidth()));
                productDo.setHeight(BigDecimal.valueOf(dto.getHeight()));
                productDo.setWeight(BigDecimal.valueOf(dto.getWeight()));
                productDo.setLengthBs(BigDecimal.valueOf(dto.getLength()).divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                productDo.setWidthBs(BigDecimal.valueOf(dto.getWidth()).divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                productDo.setHeightBs(BigDecimal.valueOf(dto.getHeight()).divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                productDo.setWeightBs(BigDecimal.valueOf(dto.getWeight()).multiply(BigDecimal.valueOf(2.2046)).setScale(3, RoundingMode.HALF_UP));
            } else {
                productDo.setLength(BigDecimal.valueOf(dto.getLength()).multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                productDo.setWidth(BigDecimal.valueOf(dto.getWidth()).multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                productDo.setHeight(BigDecimal.valueOf(dto.getHeight()).multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                productDo.setWeight(BigDecimal.valueOf(dto.getWeight()).divide(BigDecimal.valueOf(2.2046), 3, RoundingMode.HALF_UP));
                productDo.setLengthBs(BigDecimal.valueOf(dto.getLength()));
                productDo.setWidthBs(BigDecimal.valueOf(dto.getWidth()));
                productDo.setHeightBs(BigDecimal.valueOf(dto.getHeight()));
                productDo.setWeightBs(BigDecimal.valueOf(dto.getWeight()));
            }
            if (!CountryEnum.getCurrencyList().contains(dto.getCurrencyCode())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getSku());
                map.put("errorMsg", "Currency/币种 Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            /*if (!CountryEnum.getCountryList().contains(dto.getCountryOfOrigin())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getSku());
                map.put("errorMsg", "Country Code of Origin/原产地简码 Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            Integer dangerousCode = DangerousEnum.getCode(dto.getDangerousCargo());
            if (Objects.isNull(dangerousCode)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getSku());
                map.put("errorMsg", "Dangerous Goods Type/危险品类型 Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            productDo.setDangerousCargo(dangerousCode);
            productDo.setDeclarePrice(BigDecimal.valueOf(dto.getDeclarePrice()));*/
            productDo.setApproveStatus(1);
            productDo.setExemptCheck(productFreeTrial);
            if (productFreeTrial) {
                productDo.setApproveStatus(2);
            }
            baseMapper.insert(productDo);
            if (productFreeTrial) {
                //免审操作日志
                operateRecordService.operateAddRecordAndValue(
                        SysConstant.Product_Pass_Without_Trial,
                        "",
                        productDo.getId(),
                        1L,
                        "system",
                        OperateTypeEnum.Product
                );
            }
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Product_import,
                    "",
                    productDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    @Override
    public void export(ProductQueryBo bo, HttpServletResponse response) throws IOException {
        List<Long> ids = bo.getIds();
        Collection<ProductDo> productList;
        if (CollUtil.isNotEmpty(ids)) {
            productList = this.listByIds(ids);
        } else {
            productList = baseMapper.getListPage(bo);
        }
        Set<Long> pcIdList = productList.stream().map(ProductDo::getCategoryId).filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, ProductCategoryDo> categoryMatch = productCategoryDao.selectList(
                Wrappers.<ProductCategoryDo>lambdaQuery()
                        .in(CollUtil.isNotEmpty(pcIdList), ProductCategoryDo::getId, pcIdList)
        ).stream().collect(Collectors.toMap(ProductCategoryDo::getId, val -> val));
        List<ProductExportDtoV2> dtoList = new ArrayList<>(productList.size());
        for (ProductDo productDo : productList) {
//            ProductExportDtoV2 dto = new ProductExportDtoV2();
//            BeanUtils.copyProperties(productDo, dto);
            ProductExportDtoV2 dto = BeanConvert.INSTANCE.productDoToProductExportDtoV2(productDo);
            //处理分类
            if (Objects.nonNull(productDo.getCategoryId())) {
                ProductCategoryDo productCategoryDo = categoryMatch.get(productDo.getCategoryId());
                if (Objects.nonNull(productCategoryDo)) {
                    String fullPathName = productCategoryDo.getFullPathName();
                    String[] split = fullPathName.split("\\\\");
                    for (int i = 0; i < split.length; i++) {
                        if (i == 0) {
                            dto.setPrimaryCategory(split[i]);
                        } else if (i == 1) {
                            dto.setSecondaryCategory(split[i]);
                        } else if (i == 2) {
                            dto.setTertiaryCategory(split[i]);
                        }
                    }
                }
            }
            if (Objects.isNull(dto.getPrimaryCategory())) {
                dto.setPrimaryCategory("-");
            }
            if (Objects.isNull(dto.getSecondaryCategory())) {
                dto.setSecondaryCategory("-");
            }
            if (Objects.isNull(dto.getTertiaryCategory())) {
                dto.setTertiaryCategory("-");
            }
            //处理危险品
//            dto.setDangerousCargo(DangerousEnum.getDesc(productDo.getDangerousCargo()));
            //处理包材
            dto.setNeedPack(productDo.getNeedPack() ? "是" : "否");
            if (productDo.getNeedPack()) {
                List<ProductPackInfoDto> infoDtoList = JSONUtil.toList(productDo.getPackInfo(), ProductPackInfoDto.class);
                infoDtoList = infoDtoList.stream().filter(i -> StrUtil.isNotBlank(i.getPackCode())).collect(Collectors.toList());
                if (CollUtil.isEmpty(infoDtoList)) {
                    dtoList.add(dto);
                } else {
                    for (ProductPackInfoDto infoDto : infoDtoList) {
                        if (StrUtil.isNotBlank(infoDto.getPackCode())) {
                            dto.setPackageMaterialCode(infoDto.getPackCode());
                            dto.setPackageMaterialName(infoDto.getPackName());
                            dto.setWarehouseCode(infoDto.getWhCode());
                            dtoList.add(dto);
                        }
                    }
                }
            } else {
                dtoList.add(dto);
            }
        }
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        exportParams.setSheetName(bo.getSource() + "产品导出");
        ExcelUtils.exportExcel(dtoList, ProductExportDtoV2.class, "Product_Export_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"), exportParams, response, "xls");
    }

    @Override
    public void printBarCodePdf(PrintSkuBo bo, HttpServletResponse response) {
        List<PrintSkuEt> printSkuEts = new ArrayList<>();
        Map<Long, ProductDo> productIdMatch = this.listByIds(bo.getSkuList().stream().map(PrintSkuBo.SkuPrintMsg::getProductId)
                .collect(Collectors.toList())).stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        Map<Long, CompanyDo> companyIdMatch = companyService.lambdaQuery()
                .in(CompanyDo::getId, productIdMatch.values().stream().map(ProductDo::getCompanyId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        for (PrintSkuBo.SkuPrintMsg msg : bo.getSkuList()) {
            ProductDo productDo = productIdMatch.get(msg.getProductId());
            if (productDo == null) {
                continue;
            }
//            PrintSkuEt skuEt = new PrintSkuEt();
//            BeanUtils.copyProperties(productDo, skuEt);
            PrintSkuEt skuEt = BeanConvert.INSTANCE.productDoToPrintSkuEt(productDo);
            skuEt.setQty(msg.getQty());
            skuEt.setCompanyCode(companyIdMatch.getOrDefault(productDo.getCompanyId(), new CompanyDo()).getCompanyCode());
            printSkuEts.add(skuEt);
        }
        String name = "Product_Print_SKU_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
        //其他纸张类型 一张一个
        BarCodePdfUtils.printManySku(bo, printSkuEts, name, response);
    }

    @Override
    public CommonBatchOperationVo discardProduct(DiscardProductBo bo, Long userId, String username) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        if (CollUtil.isEmpty(bo.getProductIdList())) {
            return res;
        }
        ProductStockBySkuQueryBo req = new ProductStockBySkuQueryBo();
        req.setProductIds(bo.getProductIdList());
        //库存
        Map<Long, SkuStockPageDto> productIdMatchStock = productStockDao.productListPageBySku(req).stream().collect(Collectors.toMap(SkuStockPageDto::getProductId, val -> val));
        //在途产品数量
        Map<Long, ProductTransportVo> productTransportMap = productStockDao.getTransportNumBySku(bo.getProductIdList(), null).stream().collect(Collectors.toMap(ProductTransportVo::getProductId, val -> val, (v1, v2) -> {
            v1.setQty(v1.getQty() + v2.getQty());
            v1.setReservedQty(v1.getReservedQty() + v2.getReservedQty());
            return v1;
        }));
        res.setTotal(bo.getProductIdList().size());
        Map<Long, ProductDo> productDoMap = this.listByIds(bo.getProductIdList()).stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        for (Long productId : bo.getProductIdList()) {
            ProductDo productDo = productDoMap.get(productId);
            SkuStockPageDto skuStockPageDto = productIdMatchStock.get(productDo.getId());
            if (Objects.nonNull(skuStockPageDto) && skuStockPageDto.getAvailableAmount() > 0) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", productDo.getSku());
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Product_Discard_Failed_Exist_Available_Qty));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            ProductTransportVo productTransportVo = productTransportMap.get(productId);
            if (Objects.nonNull(productTransportVo)) {
                if (productTransportVo.getQty() > 0) {
                    HashMap<String, Object> map1 = new HashMap<>(4);
                    map1.put("code", productDo.getSku());
                    map1.put("errorMsg", StringsUtil.createI18Message(SysConstant.Product_Discard_Failed_Exist_Transport_Qty));
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map1);
                    continue;
                }
            }
            this.lambdaUpdate()
                    .eq(ProductDo::getId, productDo.getId())
                    .set(ProductDo::getApproveStatus, 4)
                    .set(ProductDo::getDiscardTime, DateUtil.date())
                    .set(ProductDo::getOldApproveStatus, productDo.getApproveStatus())
                    .update();
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Product_Discard,
                    "",
                    productDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    @Override
    public CommonBatchOperationVo recoverProduct(RecoverProductBo bo, Long userId, String username) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        if (CollUtil.isEmpty(bo.getProductIdList())) {
            return res;
        }
        res.setTotal(bo.getProductIdList().size());
        Map<Long, ProductDo> productDoMap = this.listByIds(bo.getProductIdList()).stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        for (Long productId : bo.getProductIdList()) {
            ProductDo productDo = productDoMap.get(productId);
            if (Objects.isNull(productDo)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", productId);
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Product_Not_Exist_Recover_Failed));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (productDo.getApproveStatus() != 4) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", productDo.getSku());
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Product_No_Discard_Status_Recover_Failed));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            this.lambdaUpdate()
                    .eq(ProductDo::getId, productDo.getId())
                    .set(ProductDo::getApproveStatus, productDo.getOldApproveStatus())
                    .set(ProductDo::getDiscardTime, null)
                    .update();
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Product_Recover,
                    "",
                    productDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    @Override
    public CommonBatchOperationVo pushProductToWms(PushProductToWmsBo bo, Long userId, String username) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<Long> productIdList = bo.getProductIdList();
        if (CollUtil.isEmpty(productIdList)) {
            return res;
        }
        res.setTotal(bo.getProductIdList().size());
        Map<Long, ProductDo> productDoMap = this.listByIds(bo.getProductIdList()).stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        for (Long productId : bo.getProductIdList()) {
            ProductDo productDo = productDoMap.get(productId);
            if (Objects.isNull(productDo)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", productId);
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Product_NotExist));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (productDo.getApproveStatus() != 3 && productDo.getApproveStatus() != 0) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", productDo.getSku());
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Product_Status_Not_Correct));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            this.lambdaUpdate()
                    .eq(ProductDo::getId, productDo.getId())
                    .set(ProductDo::getApproveStatus, 1)
                    .update();
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Product_Submission_For_Review,
                    "",
                    productDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    @Override
    public CommonBatchOperationVo deleteProduct(DeleteProductBo bo, Long userId, String username) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<Long> productIdList = bo.getProductIdList();
        if (CollUtil.isEmpty(productIdList)) {
            return res;
        }
        res.setTotal(bo.getProductIdList().size());
        Map<Long, ProductDo> productDoMap = this.listByIds(bo.getProductIdList()).stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        for (Long productId : bo.getProductIdList()) {
            ProductDo productDo = productDoMap.get(productId);
            if (Objects.isNull(productDo)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", productId);
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Product_NotExist));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (productDo.getApproveStatus() != 4) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", productDo.getSku());
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Product_No_Discard_Status_Del_Failed));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            //旧数据有用到产品则不删除数据
            List<OrderBoxProductDo> list1 = boxProductDao.selectList(Wrappers.<OrderBoxProductDo>lambdaQuery().eq(OrderBoxProductDo::getProductId, productId));
            List<OrderOutboundProductDo> list2 = outboundProductDao.selectList(Wrappers.<OrderOutboundProductDo>lambdaQuery().eq(OrderOutboundProductDo::getProductId, productId));
            List<OrderReturnProductDo> list3 = returnProductDao.selectList(Wrappers.<OrderReturnProductDo>lambdaQuery().eq(OrderReturnProductDo::getProductId, productId));
            if (CollUtil.isNotEmpty(list1) || CollUtil.isNotEmpty(list2) || CollUtil.isNotEmpty(list3)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", productDo.getSku());
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Product_Related_Order_Data_Cannot_Be_Deleted));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            this.removeById(productId);
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    @Override
    public void getOmpImportUpdateProductTemplate(HttpServletResponse response, Long tenantId, Long userId) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/OmpImportUpdateProduct.xlsx", true);

        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=OmpImportUpdateProduct.xlsx");
        Map<String, Object> sheetMap = new HashMap<>(2);
        //处理sheet(Customer客户信息)
        List<CompanyRes> companyResList = companyService.getCompanyList(userId, tenantId, "omp");
        List<Map<String, String>> companyList = new ArrayList<>();
        for (CompanyRes companyRes : companyResList) {
            Map<String, String> customerMap = new HashMap<>();
            customerMap.put("companyCode", companyRes.getCompanyCode());
            customerMap.put("companyName", companyRes.getCompanyName());
            companyList.add(customerMap);
        }
        sheetMap.put("companyList", companyList);
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, sheetMap)) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    @Override
    public void getOmsImportUpdateProductTemplate(HttpServletResponse response, Long tenantId) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/OmsImportUpdateProductV2.xlsx", true);

        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=OmsImportUpdateProduct.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, this.buildTemplateSheetData(tenantId, true))) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    private Map<String, Object> buildTemplateSheetData(Long tenantId, boolean needPack) {
        //每个sheet的数据对应一个map
        Map<String, Object> sheetMap = new HashMap<>(4);
        //处理第一个sheet(CountryCodeOfOrigin原产地简码)
        List<Map<String, String>> countryEnumList = CountryEnum.getCountryEnumList();
        sheetMap.put("countryList", countryEnumList);
        //处理第二个sheet(ProductCategory产品分类)
        //查询分类数据
        List<ProductCategoryDo> productCategoryDoList = productCategoryDao.selectList(Wrappers.<ProductCategoryDo>lambdaQuery().eq(ProductCategoryDo::getTenantId, tenantId));
        List<Map<String, String>> categoryList = new ArrayList<>();
        for (ProductCategoryDo categoryDo : productCategoryDoList) {
            String fullPathName = categoryDo.getFullPathName();
            String[] split = fullPathName.split("\\\\");
            Map<String, String> categoryMap = new HashMap<>();
            for (int i = 0; i < split.length; i++) {
                if (i == 0) {
                    categoryMap.put("primaryCategory", split[i]);
                    categoryMap.put("secondaryCategory", "");
                    categoryMap.put("tertiaryCategory", "");
                } else if (i == 1) {
                    categoryMap.put("secondaryCategory", split[i]);
                } else if (i == 2) {
                    categoryMap.put("tertiaryCategory", split[i]);
                }
            }
            categoryList.add(categoryMap);
        }
        sheetMap.put("categoryList", categoryList);
        if (needPack) {
            //处理第三个sheet(PackageMaterial包材)
            //查询包材数据
            List<MaterialDo> materialDoList = materialDao.selectList(Wrappers.<MaterialDo>lambdaQuery().eq(MaterialDo::getTenantId, tenantId));
            Map<Long, WarehouseDo> whIdMatch = warehouseDao.selectList(Wrappers.<WarehouseDo>lambdaQuery().eq(WarehouseDo::getTenantId, tenantId)).stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
            List<Map<String, String>> packageMaterialList = new ArrayList<>();
            for (MaterialDo materialDo : materialDoList) {
                Map<String, String> packageMaterialMap = new HashMap<>(4);
                packageMaterialMap.put("materialName", materialDo.getMaterialName());
                packageMaterialMap.put("materialCode", materialDo.getMaterialCode());
                packageMaterialMap.put("warehouseCode", whIdMatch.get(materialDo.getWhId()).getWhCode());
                packageMaterialList.add(packageMaterialMap);
            }
            sheetMap.put("packageMaterialList", packageMaterialList);
        }
        return sheetMap;
    }

    @Override
    public CommonBatchOperationVo omsImportUpdateProduct(MultipartFile file, Long tenantId, Long companyId, Long userId, String username) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<ProductImportUpdateOmsDtoV2> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, ProductImportUpdateOmsDtoV2.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream().filter(
                i -> StrUtil.isNotBlank(i.getSku())
//                     && StrUtil.isNotBlank(i.getNeedPackageMaterial())
        ).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", "Exception");
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        //查询sku
        Map<String, ProductDo> skuMatch = this.lambdaQuery()
                .eq(ProductDo::getTenantId, tenantId)
                .eq(ProductDo::getCompanyId, companyId)
                .in(ProductDo::getSku, importDtoList.stream().map(ProductImportUpdateOmsDtoV2::getSku).collect(Collectors.toList())).list().stream().collect(Collectors.toMap(ProductDo::getSku, val -> val));
        //查询包材
        List<MaterialDo> materialDoList = materialDao.selectList(Wrappers.<MaterialDo>lambdaQuery().eq(MaterialDo::getTenantId, tenantId));
        List<String> materialCodeList = materialDoList.stream().map(MaterialDo::getMaterialCode).collect(Collectors.toList());
        List<String> materialNameList = materialDoList.stream().map(MaterialDo::getMaterialName).collect(Collectors.toList());
        //查询仓库
        Map<String, WarehouseDo> whCodeMatch = warehouseDao.selectList(Wrappers.<WarehouseDo>lambdaQuery().eq(WarehouseDo::getTenantId, tenantId)).stream().collect(Collectors.toMap(WarehouseDo::getWhCode, val -> val));
        for (ProductImportUpdateOmsDtoV2 dto : importDtoList) {
            ProductDo productDo = skuMatch.get(dto.getSku());
            if (Objects.isNull(productDo)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getSku());
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Product_NotExist));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
//            BeanUtils.copyProperties(dto, productDo);
            BeanConvert.INSTANCE.productImportUpdateOmsDtoV2ToProductDo(dto, productDo);
            if (StrUtil.isNotBlank(dto.getPrimaryCategory()) || StrUtil.isNotBlank(dto.getSecondaryCategory()) || StrUtil.isNotBlank(dto.getTertiaryCategory())) {
                String category = StrUtil.isNotBlank(dto.getPrimaryCategory())
                        ? dto.getPrimaryCategory() + (StrUtil.isNotBlank(dto.getSecondaryCategory()) ? "\\" + dto.getSecondaryCategory() + (StrUtil.isNotBlank(dto.getTertiaryCategory()) ? "\\" + dto.getTertiaryCategory() : "") : "")
                        : "";
                if (StrUtil.isNotBlank(category)) {
                    ProductCategoryDo productCategoryDo = productCategoryDao.selectOne(Wrappers.<ProductCategoryDo>lambdaQuery()
                            .eq(ProductCategoryDo::getTenantId, tenantId)
                            .eq(ProductCategoryDo::getFullPathName, category));
                    if (Objects.isNull(productCategoryDo)) {
                        HashMap<String, Object> map = new HashMap<>(4);
                        map.put("code", dto.getSku());
                        map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Product_Category_Not_Exist));
                        res.setFailed(res.getFailed() + 1);
                        res.getFailedList().add(map);
                        continue;
                    }
                    productDo.setCategoryId(productCategoryDo.getId());
                }
            }
            if (StrUtil.isNotBlank(dto.getFnSkuDto())) {
                productDo.setFnSku(StrUtil.isNotBlank(productDo.getFnSku()) ? productDo.getFnSku() + "," + dto.getFnSkuDto() : dto.getFnSkuDto());
            }
            if (StrUtil.isNotBlank(dto.getOtherBarcodeDto())) {
                productDo.setOtherBarcode(StrUtil.isNotBlank(productDo.getOtherBarcode()) ? productDo.getOtherBarcode() + "," + dto.getOtherBarcodeDto() : dto.getOtherBarcodeDto());
            }
            /*if (StrUtil.isNotBlank(dto.getCountryOfOrigin()) && !CountryEnum.getCountryList().contains(dto.getCountryOfOrigin())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getSku());
                map.put("errorMsg", "Country Code of Origin/原产地简码不存在,请检查");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }*/
            if (StrUtil.isNotBlank(dto.getNeedPackageMaterial()) && "是".equals(dto.getNeedPackageMaterial())) {
                if (StrUtil.isEmpty(dto.getMaterialCode()) || StrUtil.isEmpty(dto.getMaterialName()) || StrUtil.isEmpty(dto.getWarehouseCode())) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getSku());
                    map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Material_Info_Incomplete));
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                }
                if (!materialCodeList.contains(dto.getMaterialCode())) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getSku());
                    map.put("errorMsg", "Package Material Code/包材编码 Wrong,Please check!");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                }
                if (!materialNameList.contains(dto.getMaterialName())) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getSku());
                    map.put("errorMsg", "Package Material Name/包材名称 Wrong,Please check!");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                }
                if (Objects.isNull(whCodeMatch.get(dto.getWarehouseCode()))) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getSku());
                    map.put("errorMsg", "Warehouse Code/仓库代码 Wrong,Please check!");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                }
                Optional<MaterialDo> materialDoOptional = materialDoList.stream().filter(materialDo ->
                        materialDo.getMaterialCode().equals(dto.getMaterialCode())
                        && materialDo.getMaterialName().equals(dto.getMaterialName())
                        && materialDo.getWhId().equals(whCodeMatch.getOrDefault(dto.getWarehouseCode(), null).getId())).findFirst();
                if (!materialDoOptional.isPresent()) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getSku());
                    map.put("errorMsg", "Package Material/包材 Wrong,Please check!");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                }
                List<ProductPackInfoDto> infoDtoList;
                if (productDo.getNeedPack()) {
                    infoDtoList = JSONUtil.toList(productDo.getPackInfo(), ProductPackInfoDto.class);
                    for (ProductPackInfoDto infoDto : infoDtoList) {
                        if (infoDto.getWhCode().equals(dto.getWarehouseCode())) {
                            infoDto.setPackCode(materialDoOptional.get().getMaterialCode());
                            infoDto.setPackName(materialDoOptional.get().getMaterialName());
                        } else {
                            infoDto.setPackCode("");
                            infoDto.setPackName("");
                        }
                    }
                } else {
                    infoDtoList = new ArrayList<>();
                }
                // 未匹配的仓库
                whCodeMatch.values().stream().filter(item -> CollUtil.isEmpty(infoDtoList) || infoDtoList.stream().noneMatch(infoDto -> infoDto.getWhId().equals(item.getId()))).forEach(warehouseDo -> {
                    ProductPackInfoDto infoDto = new ProductPackInfoDto();
                    infoDto.setWhId(warehouseDo.getId());
                    infoDto.setWhCode(warehouseDo.getWhCode());
                    infoDto.setWhName(warehouseDo.getWhNameCn());
                    if (infoDto.getWhCode().equals(dto.getWarehouseCode())) {
                        infoDto.setPackCode(materialDoOptional.get().getMaterialCode());
                        infoDto.setPackName(materialDoOptional.get().getMaterialName());
                    } else {
                        infoDto.setPackCode("");
                        infoDto.setPackName("");
                    }
                    infoDtoList.add(infoDto);
                });
                if (CollUtil.isNotEmpty(infoDtoList)) {
                    productDo.setPackInfo(JSONUtil.toJsonStr(infoDtoList));
                } else {
                    productDo.setPackInfo("[]");
                }
                productDo.setNeedPack(true);
            }
            this.updateById(productDo);
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Product_import_Update_Oms,
                    "",
                    productDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.Product
            );
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    @Override
    public List<ProductMsgVo> getProductWeight(List<Long> productIds, Long companyId) {
        if (productIds == null || productIds.isEmpty()) {
            return new ArrayList<>();
        }
        List<ProductDo> productDos = baseMapper.selectList(new LambdaQueryWrapper<ProductDo>().in(ProductDo::getId, productIds)
                .eq(ProductDo::getCompanyId, companyId));
        List<ProductMsgVo> voList = new ArrayList<>();
        for (ProductDo productDo : productDos) {
            BigDecimal weight = productDo.getWmsWeight() == null ? productDo.getWeight() : productDo.getWmsWeight();
            voList.add(new ProductMsgVo(productDo.getId(), weight == null ? BigDecimal.valueOf(0) : weight));
        }
        return voList;
    }

    @Override
    public void getImportProductTemplate(HttpServletResponse response, Long tenantId) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/Product_ImportV2.xlsx", true);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=Product_Import.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, this.buildTemplateSheetData(tenantId, false))) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }
}
